<?php
/**
 * zen_ventana_mysql_admin.php
 * @author Juan Belon
 * @access public
 * @copyright LGPL, GPL
 * @package zenphp_generador
 * @version 0.1.1
 * @uses zenphp FrameWork
 * @link http://csl2-zenphp.forja.rediris.es
 * @link http://www.zenphp.es
 * @link https://forja.rediris.es/projects/csl2-zenphp/
 * @see zen_analizador_cartucho
 * @magic Clase que crea una ventana para administrar una conexión a una base de datos y
 *  realizar operaciones como en PhpMyAdmin pero visualmente
 */
// +----------------------------------------------------------------------
// | zenphp.es
// +----------------------------------------------------------------------
/*-----------------------------------------------
Idea original: Bart Veraart ( http://www.weberdev.com/get_example-3312.html )
-----------------------------------------------*/
class zen_gtk_ventana_mysql_admin {
	/**
	* Para crear la ventana pequeña de login:
	* @var GtkWindow
	*/
	var $ventana_login;
	/**
	 * Contenedor vertical
	 * @var GtkVBox
	 */
	var $vbox;
	/**
	 * Contenedor horizontal
	 * @var GtkHBox
	 */
	var $hbox_top;
	/**
	 * Etiqueta superior
	 *
	 * @var GtkLabel
	 */
	var $label_top;
	/**
	 * Campo de texto superior
	 * @var GtkEntry
	 */
	var $txt_usuario;
	/**
	 * Caja de en medio
	 * @var GtkHBox
	 */
	var $hbox_middle;
	/**
	 * Etiqueta de en medio
	 * @var GtkLabel
	 */
	var $label_middle;
	/**
	 * Caja de texto de en medio para el password
	 * @var GtkEntry
	 */
	var $txt_contrasena;
	/**
	 * Segunda contenedor en medio
	 * @var GtkHBox
	 */
	var $hbox_middle2;
	/**
	 * Segunda etiqueta de en medio para el servidor
	 * @var GtkLabel
	 */
	var $label_middle2;
	/**
	 * Segunda caja de texto en medio para el servidor
	 * @var GtkEntry
	 */
	var $txt_servidor;
	/**
	 * Contenedor debajo
	 * @var GtkHBox
	 */
	var $hbox_bottom;
	/**
	 * Botón para el login
	 *
	 * @var GtkButton
	 */
	var $login_button;
	/**
	* Título de la ventana
	* @var str
	*/
	var $titulo_ventana = "Administrador mySQL";
	/**
	* Fichero para guardar los logs
	* @var str
	*/
	var $fichero_log = "logs/zen_mysql_consultas.log";
	/**
	 * Constructor
	 * @param str $usuario
	 * @param str $password
	 * @param str $servidor
	 *
	 * @return zen_gtk_ventana_mysql_admin
	 */
	function zen_gtk_ventana_mysql_admin($usuario="juaxix",$password="mipassword",$servidor="localhost"){
		/*Para crear la ventana pequeña de login:*/
		$this->ventana_login=$this->crear_ventana_estandar("Login","Login");
		$this->ventana_login->connect('destroy',array($this,'salir'));
		$this->ventana_login->set_default_size(250,100);
		$this->vbox=$this->ventana_login->child->child;
		//Contenedor horizontal
		$this->hbox_top=& new GtkHBox(false,5);
		$this->hbox_top->set_homogeneous(true);
		//Contenedor vertical
		$this->vbox->pack_start($this->hbox_top,false,false);
		$this->hbox_top->show();
		//Etiqueta de arriba
		$this->label_top=& new GtkLabel("Usuario:");
		$this->hbox_top->pack_start($this->label_top,false,false);
		$this->label_top->show();
		//Caja de arriba
		$this->txt_usuario=& new GtkEntry();
		$this->txt_usuario->set_text($usuario);
		$this->hbox_top->pack_start($this->txt_usuario,false,false);
		$this->txt_usuario->show();
		//Caja de en medio
		$this->hbox_middle=& new GtkHBox(false,5);
		$this->hbox_middle->set_homogeneous(true);
		$this->vbox->pack_start($this->hbox_middle,false,false);
		$this->hbox_middle->show();
		//Etiqueta de en medio
		$this->label_middle=& new GtkLabel("Password:");
		$this->hbox_middle->pack_start($this->label_middle,false,false);
		$this->label_middle->show();
		//Caja de en medio
		$this->txt_contrasena=& new GtkEntry();
		$this->txt_contrasena->set_text($password);
		$this->txt_contrasena->set_visibility(false);
		$this->hbox_middle->pack_start($this->txt_contrasena,false,false);
		$this->txt_contrasena->show();
		//Segunda caja de en medio para el server:
		$this->hbox_middle2=& new GtkHBox(false,5);
		$this->hbox_middle2->set_homogeneous(true);
		$this->vbox->pack_start($this->hbox_middle2,false,false);
		$this->hbox_middle2->show();
		//Segunda etiqueta de en medio
		$this->label_middle2=& new GtkLabel("Servidor:");
		$this->hbox_middle2->pack_start($this->label_middle2,false,false);
		$this->label_middle2->show();
		/*middle textfield*/
		$this->txt_servidor=& new GtkEntry();
		$this->txt_servidor->set_text($servidor);
		$this->hbox_middle2->pack_start($this->txt_servidor,false,false);
		$this->txt_servidor->show();
		//Caja de abajo
		$this->hbox_bottom=& new GtkHBox(false,5);
		$this->hbox_bottom->set_homogeneous(true);
		$this->vbox->pack_start($this->hbox_bottom,false,false);
		$this->hbox_bottom->show();
		//Botón para el login
		$this->login_button =& new GtkButton("Conectar");
		$this->login_button->connect("clicked",array($this,"hacer_login"),$this->txt_usuario,$this->txt_contrasena,$this->txt_servidor);
		$this->hbox_bottom->pack_start($this->login_button,false,false,15);
		$this->ventana_login->show_all();
		//gtk::main();
		$this->crear_log();
	}

	/**
	 * Ejecuta una consulta mySQL: si se devuelve un resultado lo muestra, en otro caso
	 * se muestran las columnas afectadas descritas en el fichero de log. Se refresca el 
         * árbol si se ha producido una alteración en la bd con create, alter o drop
	 *
	 * @param GtkButton $boton
	 * @param str $consulta_entry
	 * @param str $nombre_bd
	 * @param str $servidor
	 * @param str $usuario
	 * @param str $password
	 * @param GtkCombo $consulta_combo
	 * @param GtkCTree $ctree
	 */
	function ejecutar_consulta($boton,$consulta_entry,$nombre_bd,$servidor,$usuario,$password,$consulta_combo,$ctree){
		$conexion=mysql_connect($servidor,$usuario,$password);
		if(!$conexion) $this->alerta("No se pudo realizar la conexion a la BD");
		$consulta=$consulta_entry->get_text();
		mysql_select_db($nombre_bd,$conexion) or $this->alerta(mysql_error());

		$resul=mysql_query($consulta,$conexion) or $this->alerta(mysql_error());

		$this->actualizar_log_consultas($nombre_bd,$usuario,$consulta);

		$arbol_cambiado=false; /*keep track of changes made: if true-> refresh tree*/
		if($resul){
			if (stristr($consulta,"delete")){
				$this->alerta(str_replace("delete","deleted",$consulta));
			}
			elseif (stristr($consulta,"update")) $this->alerta("actualizados ".mysql_affected_rows()." registros");
			elseif (stristr($consulta,"insert")) $this->alerta("insertados ".mysql_affected_rows()." registros");
			elseif (stristr($consulta,"replace")) $this->alerta("reemplazados ".mysql_affected_rows()." registros");
			elseif (stristr($consulta,"select") || stristr($consulta,"describe") || stristr($consulta,"show") || stristr($consulta,"explain")){
				$ventana=$this->crear_ventana_estandar($this->titulo_ventana." - resultado de la consulta","Resultado");
				$vbox=$ventana->child->child;
				$vbox->set_homogeneous(false);
				$consulta_label=& new Gtklabel("Consulta: \n$consulta \n...devueltas ".mysql_num_rows($resul)." tuplas.");

				$consulta_label->set_line_wrap(true);
				$vbox->pack_start($consulta_label,false,false);
				$consulta_label->show();

				$scroll_window=& new GtkScrolledWindow();
				$scroll_window->set_border_width(5);
				$scroll_window->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
				$vbox->pack_start($scroll_window,true,true);

				for($i=0;$i<mysql_num_fields($resul);$i++){
					$nombres_campos[$i]=mysql_field_name($resul,$i);
				}
				//Evitar warning deprecated del constructor:
				@$clist=& new GtkClist(mysql_num_fields($resul),$nombres_campos);

				$scroll_window->add($clist);
				$clist->show();
				$n = mysql_num_rows($resul);
				for($i=0;$i<$n;$i++){
					@$col=$clist->append(mysql_fetch_row($resul));
					@$clist->set_column_auto_resize($col,true);
				}

				$ventana->show_all();
			}

			else if (stristr($consulta,"drop")) {
				$arbol_cambiado=true;
				$this->alerta(str_replace("drop","borradas",$consulta));
			}

			else if (stristr($consulta,"alter")) {
				$arbol_cambiado=true;
				$this->alerta(str_replace("alter","modificadas",$consulta));
			}

			else if (stristr($consulta,"create")) {
				$arbol_cambiado=true;
				$this->alerta(str_replace("create","creadas",$consulta));
			}
		}
		mysql_close($conexion);
		$tabla_afectada = "";
		if($arbol_cambiado==true) {
			$tbl_str="table";
			$tbl_strp=strpos($consulta,$tbl_str);

			$tabla_afectada=substr($consulta,$tbl_strp+strlen($tbl_str)+1);
			$pos=strpos($tabla_afectada," ");
			$tabla_afectada=substr($tabla_afectada,0,$pos);
			//$this->alerta("affected tabel: ".$tabla_afectada);
		}

		if ($arbol_cambiado==true) $this->refrescar_ctree($ctree,$nombre_bd,$servidor,$usuario,$password,$tabla_afectada);
		$this->refrescar_combo_consultas($consulta_combo,$nombre_bd,$usuario);
	}

	/**
	* 
	* @param GtkButton $boton
	* @param GtkEntry $txt_usuario
	* @param GtkEntry $txt_contrasena
	* @param GtkEntry $txt_servidor
	*/
	function hacer_login($boton,&$txt_usuario,&$txt_contrasena,&$txt_servidor){
  		$usuario=$txt_usuario->get_text();
		$contrasena=$txt_contrasena->get_text();
		$servidor=$txt_servidor->get_text();

		$conexion=mysql_connect($servidor,$usuario,$contrasena);
		if(!$conexion){
		 $this->alerta("Imposible conectar a $usuario del servidor de mysql $servidor!\n".mysql_error());
		} else {
			$rs_dbs=mysql_list_dbs($conexion);

			/*crear una ventana estándar y una vbox*/

			$ventana_datos= new GtkWindow();
			$ventana_datos->connect("destroy",array($this,"cerrar_ventana"));
			$ventana_datos->connect("delete_event",array($this,"borrar_evento"));
			$ventana_datos->set_title($this->titulo_ventana." ".$servidor);
			$ventana_datos->set_default_size(500,500);

			$ventana_datos_vbox=& new GtkVBox();

			$ventana_datos->add($ventana_datos_vbox);
			$ventana_datos_vbox->show();

			$notebook= new GtkNotebook();
			$notebook->show();

			$notebook->set_tab_pos(Gtk::POS_LEFT);
			$notebook->set_scrollable(true);

			$ventana_datos_vbox->pack_start($notebook,true,true,0);

			$notebook->set_border_width(10);
			$notebook->realize();

			/* crear las páginas para las pestañas*/
			$n = mysql_num_rows($rs_dbs);
			for($i=0;$i<$n;$i++){
				$row=mysql_fetch_object($rs_dbs);
				$frame=& new GtkFrame($row->Database);
				$vbox =& new GtkVBox(true, 0);
				$vbox->set_homogeneous(false);
				$frame->add($vbox);
				$hbox =& new GtkHbox(true, 0);
				$vbox->pack_start($hbox, true, true);
				$rs_tables=mysql_list_tables($row->Database);
				/*es mejor usar una ventana de scroll para que no se desmaquete todo y se salga de la pantalla*/
				$scroll_window=& new GtkScrolledWindow();
				$scroll_window->set_border_width(5);
				$scroll_window->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_ALWAYS);
				$hbox->pack_start($scroll_window,false,true);

				@$ctree =& new GtkCTree(2, 0, array('Tabla','Tipo'));
				$scroll_window->add($ctree);
				$ctree->show();

				$ctree->set_column_auto_resize(0, true);
				$ctree->set_column_width(1, 200);
				$ctree->set_selection_mode(Gtk::SELECTION_EXTENDED);
				$ctree->set_line_style(Gtk::CTREE_LINES_SOLID);

				/*árbol principal*/

				$this->refrescar_ctree($ctree,$row->Database,$servidor,$usuario,$contrasena,"");

				$consulta_hbox=& new GtkHBox();
				$vbox->pack_start($consulta_hbox,false,false);

				$consulta_hbox->show();

				$consulta_label=& new GtkLabel("Consulta:  ");
				$consulta_hbox->pack_start($consulta_label,false,false);
				$consulta_label->show();

				$consulta_historial= array();
				$manejador=fopen($this->fichero_log,"r") or $this->alerta("No se pudo abrir el fichero de log ".$this->fichero_log);
				while(!feof($manejador)){
					$consulta_log_line=fgets($manejador,4096);
					$consulta_log_line_array=explode(":",$consulta_log_line);
					if($consulta_log_line_array[0]==$row->Database && $consulta_log_line_array[1]==$usuario)
					$consulta_historial[]=trim($consulta_log_line_array[2]);
				}

				$consulta_combo=& new GtkCombo();

				$consulta_combo->set_popdown_strings($consulta_historial);
				$consulta_entry = $consulta_combo->entry;
				$consulta_entry->set_text('');
				$consulta_entry->set_max_length(5000);
				$consulta_entry->select_region(0, -1);
				$consulta_hbox->pack_start($consulta_combo,true,true);
				$consulta_combo->show();
				$consulta_button=& new GtkButton("Lanzar!");
				$consulta_button->connect('clicked',array($this,'ejecutar_consulta'),$consulta_entry,$row->Database,$servidor,$usuario,$contrasena,$consulta_combo,$ctree);
				$consulta_hbox->pack_start($consulta_button,false,false);
				$consulta_button->show();

				$frame->show_all();

				$label_box =& new GtkHBox(false, 0);

				$label =& new GtkLabel($row->Database);
				$label_box->pack_start($label, false, true, 0);
				$label_box->show_all();

				$menu_box =& new GtkHBox(false, 0);

				$label =& new GtkLabel($row->Database);
				$menu_box->pack_start($label, false, true, 0);
				$menu_box->show_all();

				$notebook->append_page_menu($frame, $label_box, $menu_box);
			}

			$ventana_datos->show();
			mysql_close($conexion);
		}
	}
	/**
	* Evento para borrar un evento XD
	*/
	function borrar_evento(){
		return false;
	}
	/**
	* Evento de cerrar ventana, en realidad sólo la esconde...
	* @param Object
	*/
	function cerrar_ventana($widget){
		$ventana = $widget->get_toplevel();
		$ventana->hide();
	}
	/**
	* Evento de salir de la ventana de login
	*/
	function salir(){
		if (ZF_GTK_MODO_DEBUG)
		 print("Cerrando mysqladmin...\n");
	}
	/**
	* Función para mostrar información por pantalla a través de un cuadro de diálogo
	* @param str $mensaje
	*/
	function alerta($mensaje){
		$mensaje=wordwrap($mensaje,30);
		$dialogo =& new GtkDialog();
		@$dialogo->set_policy(true, false, false);
		
		$dialogo->set_position(Gtk::WIN_POS_CENTER);
		//$dialogo->connect_object("destroy","cerrar_ventana");

		$dialogo_vbox = $dialogo->vbox;
		$dialogo_action_area = $dialogo->action_area;

		$usuario_info_label =& new GtkLabel($mensaje);
		$dialogo_vbox->pack_start($usuario_info_label);

		$boton_ok =& new GtkButton("OK");
		$boton_ok->connect("clicked",array($this,"cerrar_ventana"));
		$dialogo_action_area->pack_start($boton_ok);

		$dialogo->show_all();
	}
	/**
	* Crea una ventana estándar con el $título_ventana en el título, $titulo_marco en el marco y la devuelve
	* @param str $titulo_ventana
	* @param str $titulo_marco
	* @return GtkWindow
	*/
	function crear_ventana_estandar($titulo_ventana,$titulo_marco){
		$ventana=& new GtkWindow();
		$ventana->set_default_size(700,500);
		$ventana->connect("destroy",array($this,"cerrar_ventana"));
		$ventana->connect("delete_event",array($this,"borrar_evento"));
		$ventana->set_title($this->titulo_ventana." ".$titulo_ventana);

		$frame=& new GtkFrame($titulo_marco);
		$ventana->add($frame);
		$frame->show();

		$vbox=& new GtkVBox();
		$vbox->set_homogeneous(false);
		$frame->add($vbox);
		$vbox->show();

		return $ventana;
	}
	/**
	* Actualiza el fichero de log con la consulta pasada para la base datos especificada y el $usuario devolviendo el resultado
	* @param str $nombre_bd
	* @param str $usuario
	* @param str $consulta
	* @return bool
	*/
	function actualizar_log_consultas($nombre_bd,$usuario,$consulta){
		$manejador = null;
		if(!$this->existe_en_log("$nombre_bd:$usuario:$consulta")){
			if(!$manejador=fopen($this->fichero_log,"a+")){
				$this->alerta("No se pudo abrir el fichero de log para escribir en el");
				return false;
			}
			else fputs($manejador,"$nombre_bd:$usuario:$consulta\n");
		}
		if (!is_null($manejador)) fclose($manejador);
	}
	/**
	* Existe la línea en el fichero de log?
	* @param str $linea
	* @return bool
	*/
	function existe_en_log($linea){
		if(file_exists($this->fichero_log)){
			if(!$manejador=fopen($this->fichero_log,"r")) $this->alerta("No se pudo abrir el fichero de log ".$this->fichero_log."para lectura...");
			else {
				while(!feof($manejador)){
					$linea_log=fgets($manejador,4096);
					if(strcmp(trim($linea_log),trim($linea))==0) return true;
				}
				fclose($manejador);
			}
		}
		else $this->alerta("El fichero de log ".$this->fichero_log."no existe");
		return false;
	}
	/**
	* Crea el fichero de log
	*/
	function crear_log(){
		if (!is_dir("logs")){
		 mkdir("logs");
		} 
		if (!file_exists($this->fichero_log)) {
		 $manejador = fopen($this->fichero_log, 'w') or die("No puedo crear el fichero ".$this->fichero_log);
		 fclose($manejador);
		}
	}
	/**
	* Trunca el fichero de log si existe
	*/
	function limpiar_log (){
		if(!file_exists($this->fichero_log)) {
			$this->alerta ("El fichero de log ".$this->fichero_log." no existe");
		} else {
			$manejador=fopen($this->fichero_log,"w");
			fclose($manejador);
		}
	}
	/**
	* Cuadro de diálogo de confirmación para un mensaje
	* @param str $mensaje
	*/
	function confirmar($mensaje){
		$dialogo =& new GtkDialog();
		$dialogo->set_policy(true, false, false);
		$dialogo->set_position(Gtk::WIN_POS_CENTER);
		//$dialogo->connect_object("destroy","cerrar_ventana");

		$dialogo_vbox = $dialogo->vbox;
		$dialogo_action_area = $dialogo->action_area;

		$usuario_info_label =& new GtkLabel($mensaje);
		$dialogo_vbox->pack_start($usuario_info_label);

		$boton_ok =& new GtkButton("OK");
		$boton_ok->connect("clicked",array($this,"return_true"));
		$dialogo_action_area->pack_start($boton_ok);

		$cancel_button=& new GtkButton("Cancel");
		$cancel_button->connect("clicked",array($this,"return_false"));
		$dialogo_action_area->pack_start($cancel_button);

		$dialogo->show_all();
	}
	/**
	* Refresca el combo $consulta_combo de la base de datos $bd del $usuario
	* @param GtkCombo $consulta_combo
	* @param str $bd
	* @param str $usuario
	*/
	function refrescar_combo_consultas(&$consulta_combo,$bd,$usuario){
		$consulta_historial="";
		$manejador=fopen($this->fichero_log,"r")or $this->alerta("No se puede abrir el fichero ".$this->fichero_log);
		while(!feof($manejador)){
			$consulta_log_line=fgets($manejador,255);
			$consulta_log_line_array=explode(":",$consulta_log_line);
			if($consulta_log_line_array[0]==$bd && $consulta_log_line_array[1]==$usuario)
			$consulta_historial[]=trim($consulta_log_line_array[2]);
		}
		//$consulta_historial=reverse_array($consulta_historial);
		$consulta_historial=array_reverse($consulta_historial);
		$consulta_combo->set_popdown_strings($consulta_historial);
	}
	/**
	* Refresca un Ctree con las tablas de $bd a través del $servidor con $usuario,$contrasena para la $tabla_afectada
	* @param GtkCTree $ctree
	* @param str $bd
	* @param str $usuario
	* @param str $contrasena
	* @param str $tabla_afectada
	*/
	function refrescar_ctree($ctree,$bd,$servidor,$usuario,$contrasena,$tabla_afectada){
		$conexion=mysql_connect($servidor,$usuario,$contrasena);
		mysql_select_db($bd);

		$rs_tables=mysql_list_tables($bd);

		$ctree->freeze();
		$ctree->clear();
		$n = mysql_num_rows($rs_tables);
		for($j=0;$j<$n;$j++){
			$tablename=mysql_tablename($rs_tables,$j);

			$tablename_array=array($tablename,'');
			$parent=$ctree->insert_node(null,null,$tablename_array,5,null,null,null,null,false,false);

			$rs_field=mysql_list_fields($bd,$tablename,$conexion);

			for($k=0;$k<mysql_num_fields($rs_field);$k++){
				$field=mysql_field_name($rs_field,$k);
				$field_array=array($field,mysql_field_type($rs_field,$k)."(".mysql_field_len($rs_field,$k).") ".mysql_field_flags($rs_field,$k));

				$ctree->insert_node($parent,null,$field_array,5,null,null,null,null,true,false);
			}
			if (isset($tabla_afectada) && !empty($tabla_afectada) && $tablename==$tabla_afectada) 
			 	$ctree->expand($parent);
		}

		$ctree->thaw();
	}
	/**
	* Función que devuelve true para usar como evento
	* @return bool
	*/
	function return_true(){
		return true;
	}
	/**
	* Función que devuelve false para usar como evento
	* @return bool
	*/
	function return_false(){
		return false;
	}
}
?>